// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_LIB_PENTEST_LIB_H_
#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_LIB_PENTEST_LIB_H_

#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/ujson/ujson.h"
#include "sw/device/tests/penetrationtests/json/pentest_lib_commands.h"

typedef struct pentest_registered_alerts {
  uint32_t alerts[3];
} pentest_registered_alerts_t;

/**
 * Trigger sources.
 *
 * The trigger signal for a peripheral is based on its clkmgr_aon_idle signal.
 * These values must match the values in chiplevel.sv.tpl.
 */
typedef enum pentest_trigger_source {
  /**
   * Use AES for capture trigger.
   *
   * The trigger signal will go high 320 cycles after `dif_aes_trigger()` is
   * called and remain high until the operation is complete.
   */
  kPentestTriggerSourceAes = 0,
  /**
   * Use HMAC for capture trigger.
   */
  kPentestTriggerSourceHmac = 1,
  /**
   * Use KMAC for capture trigger.
   */
  kPentestTriggerSourceKmac = 2,
  /**
   * Use OTBN for capture trigger.
   */
  kPentestTriggerSourceOtbn = 3,
} pentest_trigger_source_t;

/**
 * Trigger type.
 */
typedef enum pentest_trigger_type {
  /**
   * Use the precise hardware capture trigger gateable by software. If selected,
   * the actual capture trigger is generated based on the clkmgr_aon_idle signal
   * of the peripheral corresponding to selected trigger source.
   *
   * Note that this is available on FPGA only.
   */
  kPentestTriggerTypeHwGated = 0,
  /**
   * Use the fully software controlled capture trigger. If selected, the
   * configured trigger source is not relevant.
   */
  kPentestTriggerTypeSw = 1,
} pentest_trigger_type_t;

/**
 * Peripherals.
 *
 * Constants below are bitmasks that can be used to specify a set of
 * peripherals.
 *
 * See also: `pentest_peripherals_t`.
 */
typedef enum pentest_peripheral {
  /**
   * EDN.
   */
  kPentestPeripheralEdn = 1 << 0,
  /**
   * CSRNG.
   */
  kPentestPeripheralCsrng = 1 << 1,
  /**
   * Entropy source.
   */
  kPentestPeripheralEntropy = 1 << 2,
  /**
   * AES.
   */
  kPentestPeripheralAes = 1 << 3,
  /**
   * HMAC.
   */
  kPentestPeripheralHmac = 1 << 4,
  /**
   * KMAC.
   */
  kPentestPeripheralKmac = 1 << 5,
  /**
   * OTBN.
   */
  kPentestPeripheralOtbn = 1 << 6,
  /**
   * Peripherals using the IO_DIV4_PERI clock (UART, GPIO, I2C, SPI Dev, ...)
   */
  kPentestPeripheralIoDiv4 = 1 << 7,
  /**
   * Peripherals using the IO_DIV2_PERI clock (SPI Host 1)
   */
  kPentestPeripheralIoDiv2 = 1 << 8,
  /**
   * USB.
   */
  kPentestPeripheralUsb = 1 << 9,
  /**
   * Peripherals using the IO_PERI clock (SPI Host 0)
   */
  kPentestPeripheralIo = 1 << 10,
} pentest_peripheral_t;

/**
 * A set of peripherals.
 *
 * This type is used for specifying which peripherals should be enabled when
 * calling `pentest_init()`. Remaining peripherals will be disabled to reduce
 * noise during SCA.
 *
 * See also: `pentest_peripheral_t`, `pentest_init()`.
 */
typedef uint32_t pentest_peripherals_t;

/**
 * A set of contexts for SW LFSR
 *
 * This type is used for specifying the context of the LFSR, with each context
 * corresponding to a different state variable
 */
typedef enum pentest_lfsr_context {
  /**
   * PRNG used for initial data sharing
   */
  kPentestLfsrMasking = 0,
  /**
   * PRNG used for determining the order of fixed and random measurements
   */
  kPentestLfsrOrder = 1,

} pentest_lfsr_context_t;

/**
 * Configures the entropy complex for OTBN tests.
 *
 * Similar to entropy_testutils_auto_mode_init(), this function inits the
 * entropy complex. However, in comparison to the function available in the
 * testutils, this function maximizes the reseed intervall to 0xffffffff.
 * This is necessary to guarantee a fixed trigger window for OTBN tests.
 *
 * @return OK or error.
 */
status_t pentest_configure_entropy_source_max_reseed_interval(void);

/**
 * Returns the registered alerts.
 *
 * If a fault injection triggered an alert, this function returns the alert ID
 * back to the host. Afterwards, the alert is cleared.
 *
 * @return A struct containing which of the alerts were triggered during the
 * test.
 */
pentest_registered_alerts_t pentest_get_triggered_alerts(void);

/**
 * Configures the alert handler for FI.
 *
 * Register all alerts and let them escalate to Phase0 only.
 */
void pentest_configure_alert_handler(void);

/**
 * Reads the device ID from the LC.
 *
 * Can be used to categorize different SCA and FI runs.
 *
 * @param device_id A buffer available to store the device id.
 * @return OK or error.
 */
status_t pentest_read_device_id(uint32_t device_id[]);

/**
 * Configures CPU for SCA and FI penetration tests.
 *
 * This function disables the iCache and the dummy instructions using the
 * CPU Control and Status Register (cpuctrlsts).
 */
void pentest_configure_cpu(void);

/**
 * Initializes the peripherals (pinmux, uart, gpio, and timer) used by SCA code.
 *
 * @param trigger Peripheral to use for the trigger signal.
 * @param enable Set of peripherals to enable. Remaining peripherals will
 * be disabled to reduce noise during SCA.
 */
void pentest_init(pentest_trigger_source_t trigger,
                  pentest_peripherals_t enable);

/**
 * Returns a handle to the intialized UART device.
 *
 * @return Handle to the initialized UART device.
 */
const dif_uart_t *pentest_get_uart(void);

/**
 * Select the capture trigger type.
 *
 * @param trigger_type The trigger type to select.
 */
void pentest_select_trigger_type(pentest_trigger_type_t trigger_type);

/**
 * Sets capture trigger high.
 *
 * The actual trigger signal used for capture is a combination (logical AND) of:
 * - the trigger gate enabled here, and
 * - the busy signal of the peripheral of interest.
 */
void pentest_set_trigger_high(void);

/**
 * Sets capture trigger low.
 */
void pentest_set_trigger_low(void);

/**
 * Functions called by `pentest_call_and_sleep()` must conform to this
 * prototype.
 */
typedef void (*sca_callee)(void);

/**
 * Calls the given function and puts Ibex to sleep.
 *
 * This function can be used to minimize noise while capturing power traces for
 * side-channel attacks, in which case `callee` would trigger the operation of
 * interest, typically a crypto operation, on the target device.
 *
 * @param callee Function to call before putting Ibex to sleep.
 * @param sleep_cycles Number of cycles to sleep.
 * @param sw_trigger Raise trigger before calling the target function.
 * @param otbn Wait until OTBN execution has finished. Not supported in
 * ENGLISH_BREAKFAST.
 */
void pentest_call_and_sleep(sca_callee callee, uint32_t sleep_cycles,
                            bool sw_trigger, bool otbn);

/**
 * Seeds the software LFSR usable e.g. for key masking.
 *
 * This functions seeds the Galois XOR type LFSR with the provided seed value.
 * Note that the LFSR itself has very low diffusion.
 *
 * @param seed The new seed value.
 * @param context Specifies which LFSR to seed.
 */
void pentest_seed_lfsr(uint32_t seed, pentest_lfsr_context_t context);

/**
 * Steps the software LFSR usable e.g. for key masking `num_steps` times.
 *
 * This function steps the Galois XOR type LFSR n times. Note that the LFSR
 * itself has very low diffusion. To improve the statistical properties, the
 * multiple steps can be run. For example, by running 32 steps it can be
 * ensured that each state bit depends on at least two previous state bits.
 *
 * @param num_steps The number of steps to run.
 * @param context Specifies which LFSR to run.

 * @return The current state of the LFSR.
 */
uint32_t pentest_next_lfsr(uint16_t num_steps, pentest_lfsr_context_t context);

/**
 * Applies a linear layer.
 *
 * This function feeds the input through a linear permutation layer. This is
 * suitable to ensure 1) that adjacent output bits of the software LFSR do not
 * go into the same S-Box (see `pentest_non_linear_layer()`) and 2) that the
 * output of S-Box(n) is not always going to be equal to the output of
 * S-Box(n+1) in the subsequent cycle. For details on how this can be achieved,
 * refer to the corresponding hardware implementation in
 * hw/ip/prim/rtl/prim_lfsr.sv.
 *
 * @param input The input value.
 *
 * @return The output of the linear layer.
 *
 */
uint32_t pentest_linear_layer(uint32_t input);

/**
 * Applies a non-linear layer.
 *
 * This function feeds the input through a non-linear layer. It is suitable to
 * improve the statistical properties of the software LFSR usable for key
 * masking, see `pentest_seed_lfsr()` and `pentest_next_lfsr()`. Internally, a
 * LUT-based AES S-Box is applied to the invididual bytes of the input word.
 *
 * In addition, the ouput bytes are XORed with the sbox[0]. This is useful to
 * ensure an all-zero seed (used to switch off key masking) also results in an
 * all-zero output of the non-linear layer.
 *
 * @param input The input value.
 *
 * @return The output of the non-linear layer.
 *
 */
uint32_t pentest_non_linear_layer(uint32_t input);

#endif  // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_LIB_PENTEST_LIB_H_
